package com.exp.security.config.sms;

import cn.hutool.core.lang.Assert;
import com.exp.security.config.DBUserDetailsManager;
import com.exp.security.config.MyAuthenticationFailureHandler;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
import org.springframework.security.config.annotation.web.configurers.SecurityContextConfigurer;
import org.springframework.security.web.authentication.*;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.context.DelegatingSecurityContextRepository;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.PortMapper;

/**
 * @author exp
 * @date 2025/1/9
 * @Description 手机号短信验证码登录配置类
 */
public class SmsLoginConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractAuthenticationFilterConfigurer<H, SmsLoginConfigurer<H>, SmsAuthenticationFilter> {


    private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;

    private SavedRequestAwareAuthenticationSuccessHandler defaultSuccessHandler = new SavedRequestAwareAuthenticationSuccessHandler();

    private AuthenticationSuccessHandler successHandler = this.defaultSuccessHandler;

    private LoginUrlAuthenticationEntryPoint authenticationEntryPoint;

    private AuthenticationFailureHandler failureHandler;

    private String failureUrl;

    public SmsLoginConfigurer() {
        super(new SmsAuthenticationFilter(), "/login/sms");
    }

    @Override
    public void init(H http) throws Exception {
        SmsAuthenticationFilter authenticationFilter = new SmsAuthenticationFilter();

        authenticationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
        this.setAuthenticationFilter(authenticationFilter);
        this.loginProcessingUrl("/sms/login");
    }

    @Override
    public void configure(H http) throws Exception {
        PortMapper portMapper = http.getSharedObject(PortMapper.class);
        if (portMapper != null) {
            this.authenticationEntryPoint.setPortMapper(portMapper);
        }

        RequestCache requestCache = http.getSharedObject(RequestCache.class);
        if (requestCache != null) {
            this.defaultSuccessHandler.setRequestCache(requestCache);
        }

        // 添加认证提供者
        DBUserDetailsManager dbUserDetailsManager = http.getSharedObject(ApplicationContext.class).getBean(DBUserDetailsManager.class);
        Assert.notNull(dbUserDetailsManager, "dbUserDetailsManager is null");
        StringRedisTemplate stringRedisTemplate = http.getSharedObject(ApplicationContext.class).getBean(StringRedisTemplate.class);
        Assert.notNull(stringRedisTemplate, "stringRedisTemplate is null");
        http.authenticationProvider(new SmsAuthenticationProvider(dbUserDetailsManager, stringRedisTemplate));

        SmsAuthenticationFilter authFilter = this.getAuthenticationFilter();
        // 设置过滤器
        authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));

        authFilter.setAuthenticationSuccessHandler(this.successHandler);
//        AuthenticationFailureHandler failureHandler = new MyAuthenticationFailureHandler();
        authFilter.setAuthenticationFailureHandler(this.failureHandler);
        if (this.authenticationDetailsSource != null) {
            authFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);
        }

        SessionAuthenticationStrategy sessionAuthenticationStrategy = http
                .getSharedObject(SessionAuthenticationStrategy.class);
        if (sessionAuthenticationStrategy != null) {
            authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
        }
        RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
        if (rememberMeServices != null) {
            authFilter.setRememberMeServices(rememberMeServices);
        }
        SecurityContextConfigurer securityContextConfigurer = http.getConfigurer(SecurityContextConfigurer.class);
        if (securityContextConfigurer != null) {
            SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
            if (securityContextRepository == null) {
                securityContextRepository = new DelegatingSecurityContextRepository(
                        new RequestAttributeSecurityContextRepository(), new HttpSessionSecurityContextRepository());
            }
            authFilter.setSecurityContextRepository(securityContextRepository);
        }
        authFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
        SmsAuthenticationFilter smsFilter = postProcess(authFilter);
        http.addFilterAfter(smsFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
        return new AntPathRequestMatcher(loginProcessingUrl, "POST");
    }

    public SmsLoginConfigurer<H> loginPage(String loginPage) {
        return super.loginPage(loginPage);
    }

    public SmsLoginConfigurer<H> phoneParameter(String phoneParameter) {
        (this.getAuthenticationFilter()).setPhoneParameter(phoneParameter);
        return this;
    }

    public SmsLoginConfigurer<H> smsCodeParameter(String smsCodeParameter) {
        (this.getAuthenticationFilter()).setSmsCodeParameter(smsCodeParameter);
        return this;
    }

    public SmsLoginConfigurer<H> failureForwardUrl(String forwardUrl) {
        this.failureHandler(new ForwardAuthenticationFailureHandler(forwardUrl));
        return this;
    }

    public SmsLoginConfigurer<H> successForwardUrl(String forwardUrl) {
        this.successHandler(new ForwardAuthenticationSuccessHandler(forwardUrl));
        return this;
    }

    private String getPhoneParameter() {
        return (this.getAuthenticationFilter()).getPhoneParameter();
    }

    private String getSmsCodeParameter() {
        return (this.getAuthenticationFilter()).getSmsCodeParameter();
    }


    public static SmsLoginConfigurer<HttpSecurity> smsLogin() {
        return new SmsLoginConfigurer<HttpSecurity>();
    }

    public final  SmsLoginConfigurer<H> failureProcessHandler(AuthenticationFailureHandler authenticationFailureHandler) {
        this.failureUrl = null;
        this.failureHandler = authenticationFailureHandler;
        return getSelf();
    }

    public final SmsLoginConfigurer<H> successProcessHandler(AuthenticationSuccessHandler successHandler) {
        this.successHandler = successHandler;
        return getSelf();
    }

    private SmsLoginConfigurer<H> getSelf() {
        return (SmsLoginConfigurer<H>) this;
    }



}
